package com.opages.mvc.adapter.common.autoconfigure;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    private ClassLoader classLoader;
    private ConfigurableListableBeanFactory beanFactory;
    private Environment environment;
    private ResourceLoader resourceLoader;
    //配置类过滤器
    private ConfigurationClassFilter configurationClassFilter;
    private static final String[] NO_IMPORTS = {};
    private static final List<String> ENPTY_CONFIG = new ArrayList<>();
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        List<String> autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry);
    }
    protected List<String> getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return ENPTY_CONFIG;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        //注解的属性标签，暂时不需要
//        configurations = removeDuplicates(configurations);
//        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//        checkExcludedClasses(configurations, exclusions);
//        configurations.removeAll(exclusions);
        //使用条件注册判断是否允许注入
        configurations = getConfigurationClassFilter().filter(configurations);
        //触发加载的配置信息类事件，即加载了自动配置类后可由实现了侦听的类进行回调
//        fireAutoConfigurationImportEvents(configurations, exclusions);
        return configurations;
    }

    protected boolean isEnabled(AnnotationMetadata metadata) {
        if (getClass() == AutoConfigurationImportSelector.class) {
            return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
        }
        return true;
    }
    protected Class<?> getAnnotationClass() {
        return EnableAutoConfiguration.class;
    }
    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }
    //将有@EnableAutoConfiguration注解的类过滤出来
    protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
        String name = getAnnotationClass().getName();
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
        return attributes;
    }
    //获取@EnableAutoConfiguration注解的SPI实现配置类
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
    //获取配置类过滤器
    private ConfigurationClassFilter getConfigurationClassFilter() {
        if (this.configurationClassFilter == null) {
            List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
            for (AutoConfigurationImportFilter filter : filters) {
                invokeAwareMethods(filter);
            }
            this.configurationClassFilter = new ConfigurationClassFilter(this.classLoader, filters);
        }
        return this.configurationClassFilter;
    }
    protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.classLoader);
    }
    //如果实现Aware接口则进行回调该类
    private void invokeAwareMethods(Object instance) {
        if (instance instanceof Aware) {
            if (instance instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) instance).setBeanClassLoader(this.classLoader);
            }
            if (instance instanceof BeanFactoryAware) {
                ((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
            }
            if (instance instanceof EnvironmentAware) {
                ((EnvironmentAware) instance).setEnvironment(this.environment);
            }
            if (instance instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
            }
        }
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory);
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 1;
    }

    protected ClassLoader getBeanClassLoader() {
        return this.classLoader;
    }

    protected final ConfigurableListableBeanFactory getBeanFactory() {
        return this.beanFactory;
    }

    public Environment getEnvironment() {
        return environment;
    }

    public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }
    /**
     * 配置类过滤器,使用AutoConfigurationMetadata进行过滤
     **/
    private static class ConfigurationClassFilter {

        private final AutoConfigurationMetadata autoConfigurationMetadata;

        private final List<AutoConfigurationImportFilter> filters;

        ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
            this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
            this.filters = filters;
        }

        List<String> filter(List<String> configurations) {
            String[] candidates = StringUtils.toStringArray(configurations);
            boolean skipped = false;
            //OnBeanCondition、OnClassCondition两种过滤类
            for (AutoConfigurationImportFilter filter : this.filters) {
                boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
                for (int i = 0; i < match.length; i++) {
                    if (!match[i]) {
                        candidates[i] = null;
                        skipped = true;
                    }
                }
            }
            if (!skipped) {
                return configurations;
            }
            List<String> result = new ArrayList<>(candidates.length);
            for (String candidate : candidates) {
                if (candidate != null) {
                    result.add(candidate);
                }
            }
            return result;
        }

    }
//    指定配置与排除类，暂时不需要
//    protected static class AutoConfigurationEntry {
//
//        private final List<String> configurations;
//
//        private final Set<String> exclusions;
//
//        private AutoConfigurationEntry() {
//            this.configurations = Collections.emptyList();
//            this.exclusions = Collections.emptySet();
//        }
//
//        /**
//         * Create an entry with the configurations that were contributed and their
//         * exclusions.
//         * @param configurations the configurations that should be imported
//         * @param exclusions the exclusions that were applied to the original list
//         */
//        AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
//            this.configurations = new ArrayList<>(configurations);
//            this.exclusions = new HashSet<>(exclusions);
//        }
//
//        public List<String> getConfigurations() {
//            return this.configurations;
//        }
//
//        public Set<String> getExclusions() {
//            return this.exclusions;
//        }
//
//    }
}
